//
//  MOS6510+Operations.m
//  C64
//
//  Created by Antonio "Willy" Malara on 19/04/09.
//  Copyright 2009 __MyCompanyName__. All rights reserved.
//

#import "MOS6510+Operations.h"

#define AssertMemoryAddress() NSAssert(operand->type == MOS6510OperandMemoryAddress, @"Operand is not a memory address")
#define AssertUnkownOpType()  NSAssert(NO, @"Operand type not expected")


@implementation MOS6510 (Operations)

- (void)NOP:(MOS6510Operand *)operand;
{
	NSLog(@"%04X NOP", self.PC);
}

- (void)JSR:(MOS6510Operand *)operand;
{
	AssertMemoryAddress();
	
	NSLog(@"%04X JSR %04X (jump taken)", self.PC, operand->value);
	
	unsigned long pcToStore = self.PC + 3;
	
	[memory setByte:(unsigned char)(pcToStore >> 8)
		  atAddress:0x0100+(S--)];
	[memory setByte:(unsigned char)(pcToStore & 0xFF)
		  atAddress:0x0100+(S--)];

	self.PC = operand->value;
}

- (void)RTS:(MOS6510Operand *)operand;
{
	NSLog(@"%04X RTS", self.PC);

	self.PCL = [memory byteAtAddress:0x0100 + (++S)];
	self.PCH = [memory byteAtAddress:0x0100 + (++S)];
}

- (void)TXS:(MOS6510Operand *)operand;
{
	NSLog(@"%04X TXS", self.PC);
	
	self.S = self.X;
}

- (void)CLD:(MOS6510Operand *)operand;
{
	NSLog(@"%04X CLD", self.PC);
	self.decimalModeBit = 0;
}

- (void)LDA:(MOS6510Operand *)operand;
{	
	unsigned char newValue;
	
	switch (operand->type)
	{
		case MOS6510OperandImmediateValue:
			newValue = operand->value;
			break;
		case MOS6510OperandMemoryAddress:
			newValue = (unsigned char) [memory byteAtAddress:operand->value]; 
			break;
		default:
			AssertUnkownOpType();
	}
	
	NSLog(@"%04X LDA %04X", self.PC, newValue);
	self.A = newValue;
	
	if ((char)newValue < 0)
		self.negativeBit = YES;
	
	if (newValue == 0)
		self.zeroBit = YES;	
}

- (void)LDX:(MOS6510Operand *)operand;
{
	unsigned char newValue;
	
	switch (operand->type)
	{
		case MOS6510OperandImmediateValue:
			newValue = operand->value;
			break;
		case MOS6510OperandMemoryAddress:
			newValue = (unsigned char) [memory byteAtAddress:operand->value];
			break;
		default:
			AssertUnkownOpType();
	}
	
	NSLog(@"%04X LDX %04X", self.PC, newValue);
	
	self.X = newValue;
	
	if ((char)newValue < 0)
		self.negativeBit = YES;
	
	if (newValue == 0)
		self.zeroBit = YES;
}

- (void)SEI:(MOS6510Operand *)operand;
{
	NSLog(@"%04X SEI", self.PC);
	self.irqMaskBit = YES;
}

-(void)CMP:(MOS6510Operand *)operand;
{
	unsigned char newValue;
	
	switch (operand->type)
	{
		case MOS6510OperandImmediateValue:
			newValue = operand->value;
			break;
		case MOS6510OperandMemoryAddress:
			newValue = (unsigned char) [memory byteAtAddress:operand->value];
			break;
		default:
			AssertUnkownOpType();
	}
	
	NSLog(@"%04X CMP %04X", self.PC, operand->value);
	
	short difference = (unsigned short) self.A - newValue;
	
	if	(difference < 0) 
		self.negativeBit = YES;
	
	if	(difference == 0)
		self.zeroBit = YES;
}

-(void)BNE:(MOS6510Operand *)operand;
{
	AssertMemoryAddress();
	
	if	(self.zeroBit == NO)
	{
		NSLog(@"%04X BNE %04X (jump taken)", self.PC, operand->value);
		self.PC = operand->value;
	}
	else
		NSLog(@"%04X BNE %04X (jump not taken)", self.PC, operand->value);
}

- (void)STX:(MOS6510Operand *)operand;
{
	AssertMemoryAddress();
	
	NSLog(@"%04X STX %04X", self.PC, operand->value);
	[memory setByte:self.X atAddress:operand->value];
}

- (void)STY:(MOS6510Operand *)operand;
{
	AssertMemoryAddress();
	
	NSLog(@"%04X STY %04X", self.PC, operand->value);
	[memory setByte:self.Y atAddress:operand->value];
}

- (void)STA:(MOS6510Operand *)operand;
{
	AssertMemoryAddress();
	
	NSLog(@"%04X STA %04X", self.PC, operand->value);
	[memory setByte:self.A atAddress:operand->value];
}

- (void)DEX:(MOS6510Operand *)operand;
{	
	NSLog(@"%04X DEX", self.PC);

	char newValue = self.X - 1;
	
	if (newValue < 0)
		self.negativeBit = YES;
	
	if (newValue == 0)
		self.zeroBit = YES;
	
	self.X = (unsigned char) newValue;
}

- (void)BEQ:(MOS6510Operand *)operand;
{
	AssertMemoryAddress();

	if (self.zeroBit == YES)
	{
		NSLog(@"%04X BEQ %04X (jump taken)", self.PC, operand->value);
		self.PC = operand->value;
	} else
		NSLog(@"%04X BEQ %04X (jump not taken)", self.PC, operand->value);
}

- (void)JMP:(MOS6510Operand *)operand;
{
	AssertMemoryAddress();
	
	NSLog(@"%04X JMP %04X (jump taken)", self.PC, operand->value);
		
	self.PC = operand->value;
}

- (void)AND:(MOS6510Operand *)operand;
{
	unsigned char newValue;
	
	switch (operand->type)
	{
		case MOS6510OperandImmediateValue:
			newValue = operand->value;
			break;
		case MOS6510OperandMemoryAddress:
			newValue = (unsigned char) [memory byteAtAddress:operand->value];
			break;
		default:
			AssertUnkownOpType();
	}
	
	NSLog(@"%04X AND %04X", self.PC, operand->value);
	
	newValue = self.A & operand->value;

	if ((char)newValue == 0)
		self.zeroBit = YES;
	
	if ((char)newValue < 0)
		self.negativeBit = YES;
	
	self.A = newValue;
}

- (void)ORA:(MOS6510Operand *)operand;
{
	unsigned char newValue;
	
	switch (operand->type)
	{
		case MOS6510OperandImmediateValue:
			newValue = operand->value;
			break;
		case MOS6510OperandMemoryAddress:
			newValue = (unsigned char) [memory byteAtAddress:operand->value];
			break;
		default:
			AssertUnkownOpType();
	}
	
	NSLog(@"%04X ORA %04X", self.PC, operand->value);
	
	newValue = self.A | operand->value;
	
	if ((char)newValue == 0)
		self.zeroBit = YES;
	
	if ((char)newValue < 0)
		self.negativeBit = YES;
	
	self.A = newValue;
}

- (void)TAX:(MOS6510Operand *)operand;
{
	NSLog(@"%04X TAX", self.PC);
	
	if ((char)self.A == 0)
		self.zeroBit = YES;
	
	if ((char)self.A < 0)
		self.negativeBit = YES;
	
	self.X = self.A;
}

- (void)TAY:(MOS6510Operand *)operand;
{
	NSLog(@"%04X TAY", self.PC);
	
	if ((char)self.A == 0)
		self.zeroBit = YES;
	
	if ((char)self.A < 0)
		self.negativeBit = YES;
	
	self.Y = self.A;
}

- (void)INY:(MOS6510Operand *)operand;
{
	NSLog(@"%04X INY", self.PC);

	self.Y = self.Y + 1;
	
	if ((char)self.Y == 0)
		self.zeroBit = YES;
	
	if ((char)self.Y < 0)
		self.negativeBit = YES;
}

- (void)LDY:(MOS6510Operand *)operand;
{
	unsigned char newValue;
	
	switch (operand->type)
	{
		case MOS6510OperandImmediateValue:
			newValue = operand->value;
			break;
		case MOS6510OperandMemoryAddress:
			newValue = (unsigned char) [memory byteAtAddress:operand->value];
			break;
		default:
			AssertUnkownOpType();
	}
	
	NSLog(@"%04X LDY %04X", self.PC, newValue);
	
	self.Y = newValue;
	
	if ((char)newValue < 0)
		self.negativeBit = YES;
	
	if (newValue == 0)
		self.zeroBit = YES;
}

- (void)INC:(MOS6510Operand *)operand;
{
	AssertMemoryAddress();
	
	NSLog(@"%04X INC %04X", self.PC, operand->value);
	
	unsigned char value =  [memory byteAtAddress:operand->value];
	value++;
	
	if ((char)value < 0)
		self.negativeBit = YES;
	
	if (value == 0)
		self.zeroBit = YES;
	
	[memory setByte:value atAddress:operand->value];
}

@end
